0%

ZYNQ嵌入式之AXI DMA(基于PYNQ-Z2开发板)

本节对AXI DMA的进行了基本介绍,并通过AXI DMA环路实验简单演示了AXI DMA IP核的使用

AXI DMA简介

  • AXI DMA:AXI Direct Memory Access 直接内存访问

    • AXI DMA 为内存和AXI4—Stream外设之间提供了高带宽的直接内存访问
    • 其可选的S/G功能可以将CPU从数据搬运任务中解放出来
    • AXI DMA 通过AXI4-Lite接口对寄存器做一些配置和获取
    • 使用AXI_DMA可以大大提高数据传输的效率和速度。它通过直接访问主存中的数据,避免了CPU的介入,从而实现了高速、并行的数据传输。而且,由于AXI_DMA是专门设计用于处理数据传输的协议,它提供了一系列优化的特性,如数据缓冲、数据对齐、FIFO和错误处理等,可以更好地满足系统的性能需求
  • MM2S:MemoryMap to Stream ,存储器映射(AXI4—Full)到 AXI4—Stream

  • S2MM:Stream to MemoryMap ,AXI4—Stream到存储器映射(AXI4—Full)

  • AXI DMA 编程顺序

    • Direct Register Mode (简单DMA)
      • 此模式提供了在MM2S和S2MM通道上进行简单DMA传输的配置,只需较少的FPGA资源,通过访问DMACR、源地址或者目的地址和长度寄存器发起DMA传输。当传输完成后,如果使能了产生中断输出,那么DMASR寄存器相关联的通道位会有效
    • DMA的MM2S(存储器映射到Stream)通道的启动顺序
      • 开启/使能MM2S通道
      • 如果需要的话,可以使能中断
      • 写一个有效的源地址到MM2S_SA寄存器:如果没有使能DRE的功能,在指定起始地址时,需要注意字节地址对齐,哪些地址是对齐或者不对齐的, 取决于Stream流的数据位宽
      • 写传输的字节数到MM2S_LENGTH寄存器:一个长度为0的值是无效的,而一个非0值,将会决定存储器映射到Stream流的数据个数。需要注意的是,必须最后一个配置MM2S_LENGTH寄存器,而其他寄存器的配置顺序没有要求
  • S/G模式

    • 它把传输的基本参数存储在内存中,这些参数就是被称为BD(Buffer Descriptor ),在工作时,通过SG接口加载和更新BD中的状态。

AXI DMA环路测试实验

  • 实验任务:使用PL的AXI DMA IP核从DDR3中读取数据,并将数据写回到DDR3中

    image-20231002144918385

1.Vivado中的相关配置

  • ZYNQ processing system的配置:自动配置好后,还需要加上如下配置

    image-20231002145724577 image-20231002145902728
  • 添加AXI4-Stream Data FIFO IP核模拟一个stream流外设

  • 添加Concat ip核合并中断端口

  • 最终Block框图如下:

    image-20231002151427960

2.AXI DMA IP核的使用

image-20231002144003054

3.Vitis中的代码编写

  • 使用Xilinx提供的简单DMA中断示例代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    #include "xaxidma.h"
    #include "xparameters.h"
    #include "xil_exception.h"
    #include "xscugic.h"

    /************************** Constant Definitions *****************************/

    #define DMA_DEV_ID XPAR_AXIDMA_0_DEVICE_ID
    #define RX_INTR_ID XPAR_FABRIC_AXIDMA_0_S2MM_INTROUT_VEC_ID
    #define TX_INTR_ID XPAR_FABRIC_AXIDMA_0_MM2S_INTROUT_VEC_ID
    #define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
    #define DDR_BASE_ADDR XPAR_PS7_DDR_0_S_AXI_BASEADDR //0x00100000
    #define MEM_BASE_ADDR (DDR_BASE_ADDR + 0x1000000) //0x01100000
    #define TX_BUFFER_BASE (MEM_BASE_ADDR + 0x00100000) //0x01200000
    #define RX_BUFFER_BASE (MEM_BASE_ADDR + 0x00300000) //0x01400000
    #define RESET_TIMEOUT_COUNTER 10000 //复位时间
    #define TEST_START_VALUE 0x0 //测试起始值
    #define MAX_PKT_LEN 0x100 //发送包长度

    /************************** Function Prototypes ******************************/

    static int check_data(int length, u8 start_value);
    static void tx_intr_handler(void *callback);
    static void rx_intr_handler(void *callback);
    static int setup_intr_system(XScuGic * int_ins_ptr, XAxiDma * axidma_ptr,
    u16 tx_intr_id, u16 rx_intr_id);
    static void disable_intr_system(XScuGic * int_ins_ptr, u16 tx_intr_id,
    u16 rx_intr_id);

    /************************** Variable Definitions *****************************/

    static XAxiDma axidma; //XAxiDma实例
    static XScuGic intc; //中断控制器的实例
    volatile int tx_done; //发送完成标志
    volatile int rx_done; //接收完成标志
    volatile int error; //传输出错标志

    /************************** Function Definitions *****************************/

    int main(void)
    {
    int i;
    int status;
    u8 value;
    u8 *tx_buffer_ptr;
    u8 *rx_buffer_ptr;
    XAxiDma_Config *config;

    tx_buffer_ptr = (u8 *) TX_BUFFER_BASE;
    rx_buffer_ptr = (u8 *) RX_BUFFER_BASE;

    xil_printf("\r\n--- Entering main() --- \r\n");

    config = XAxiDma_LookupConfig(DMA_DEV_ID);
    if (!config) {
    xil_printf("No config found for %d\r\n", DMA_DEV_ID);
    return XST_FAILURE;
    }

    //初始化DMA引擎
    status = XAxiDma_CfgInitialize(&axidma, config);
    if (status != XST_SUCCESS) {
    xil_printf("Initialization failed %d\r\n", status);
    return XST_FAILURE;
    }

    if (XAxiDma_HasSg(&axidma)) {
    xil_printf("Device configured as SG mode \r\n");
    return XST_FAILURE;
    }

    //建立中断系统
    status = setup_intr_system(&intc, &axidma, TX_INTR_ID, RX_INTR_ID);
    if (status != XST_SUCCESS) {
    xil_printf("Failed intr setup\r\n");
    return XST_FAILURE;
    }

    //初始化标志信号
    tx_done = 0;
    rx_done = 0;
    error = 0;

    value = TEST_START_VALUE;
    for (i = 0; i < MAX_PKT_LEN; i++) {
    tx_buffer_ptr[i] = value;
    value = (value + 1) & 0xFF;
    }

    Xil_DCacheFlushRange((UINTPTR) tx_buffer_ptr, MAX_PKT_LEN); //刷新Data Cache

    status = XAxiDma_SimpleTransfer(&axidma, (UINTPTR) tx_buffer_ptr,
    MAX_PKT_LEN, XAXIDMA_DMA_TO_DEVICE);
    if (status != XST_SUCCESS) {
    return XST_FAILURE;
    }

    status = XAxiDma_SimpleTransfer(&axidma, (UINTPTR) rx_buffer_ptr,
    MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);
    if (status != XST_SUCCESS) {
    return XST_FAILURE;
    }

    Xil_DCacheFlushRange((UINTPTR) rx_buffer_ptr, MAX_PKT_LEN); //刷新Data Cache
    while (!tx_done && !rx_done && !error)
    ;
    //传输出错
    if (error) {
    xil_printf("Failed test transmit%s done, "
    "receive%s done\r\n", tx_done ? "" : " not",
    rx_done ? "" : " not");
    goto Done;
    }

    //传输完成,检查数据是否正确
    status = check_data(MAX_PKT_LEN, TEST_START_VALUE);
    if (status != XST_SUCCESS) {
    xil_printf("Data check failed\r\n");
    goto Done;
    }

    xil_printf("Successfully ran AXI DMA Loop\r\n");
    disable_intr_system(&intc, TX_INTR_ID, RX_INTR_ID);

    Done: xil_printf("--- Exiting main() --- \r\n");
    return XST_SUCCESS;
    }

    //检查数据缓冲区
    static int check_data(int length, u8 start_value)
    {
    u8 value;
    u8 *rx_packet;
    int i = 0;

    value = start_value;
    rx_packet = (u8 *) RX_BUFFER_BASE;
    for (i = 0; i < length; i++) {
    if (rx_packet[i] != value) {
    xil_printf("Data error %d: %x/%x\r\n", i, rx_packet[i], value);
    return XST_FAILURE;
    }
    value = (value + 1) & 0xFF;
    }

    return XST_SUCCESS;
    }

    //DMA TX中断处理函数
    static void tx_intr_handler(void *callback)
    {
    int timeout;
    u32 irq_status;
    XAxiDma *axidma_inst = (XAxiDma *) callback;

    //读取待处理的中断
    irq_status = XAxiDma_IntrGetIrq(axidma_inst, XAXIDMA_DMA_TO_DEVICE);
    //确认待处理的中断
    XAxiDma_IntrAckIrq(axidma_inst, irq_status, XAXIDMA_DMA_TO_DEVICE);

    //Tx出错
    if ((irq_status & XAXIDMA_IRQ_ERROR_MASK)) {
    error = 1;
    XAxiDma_Reset(axidma_inst);
    timeout = RESET_TIMEOUT_COUNTER;
    while (timeout) {
    if (XAxiDma_ResetIsDone(axidma_inst))
    break;
    timeout -= 1;
    }
    return;
    }

    //Tx完成
    if ((irq_status & XAXIDMA_IRQ_IOC_MASK))
    tx_done = 1;
    }

    //DMA RX中断处理函数
    static void rx_intr_handler(void *callback)
    {
    u32 irq_status;
    int timeout;
    XAxiDma *axidma_inst = (XAxiDma *) callback;

    irq_status = XAxiDma_IntrGetIrq(axidma_inst, XAXIDMA_DEVICE_TO_DMA);
    XAxiDma_IntrAckIrq(axidma_inst, irq_status, XAXIDMA_DEVICE_TO_DMA);

    //Rx出错
    if ((irq_status & XAXIDMA_IRQ_ERROR_MASK)) {
    error = 1;
    XAxiDma_Reset(axidma_inst);
    timeout = RESET_TIMEOUT_COUNTER;
    while (timeout) {
    if (XAxiDma_ResetIsDone(axidma_inst))
    break;
    timeout -= 1;
    }
    return;
    }

    //Rx完成
    if ((irq_status & XAXIDMA_IRQ_IOC_MASK))
    rx_done = 1;
    }

    //建立DMA中断系统
    // @param int_ins_ptr是指向XScuGic实例的指针
    // @param AxiDmaPtr是指向DMA引擎实例的指针
    // @param tx_intr_id是TX通道中断ID
    // @param rx_intr_id是RX通道中断ID
    // @return:成功返回XST_SUCCESS,否则返回XST_FAILURE
    static int setup_intr_system(XScuGic * int_ins_ptr, XAxiDma * axidma_ptr,
    u16 tx_intr_id, u16 rx_intr_id)
    {
    int status;
    XScuGic_Config *intc_config;

    //初始化中断控制器驱动
    intc_config = XScuGic_LookupConfig(INTC_DEVICE_ID);
    if (NULL == intc_config) {
    return XST_FAILURE;
    }
    status = XScuGic_CfgInitialize(int_ins_ptr, intc_config,
    intc_config->CpuBaseAddress);
    if (status != XST_SUCCESS) {
    return XST_FAILURE;
    }

    //设置优先级和触发类型
    XScuGic_SetPriorityTriggerType(int_ins_ptr, tx_intr_id, 0xA0, 0x3);
    XScuGic_SetPriorityTriggerType(int_ins_ptr, rx_intr_id, 0xA0, 0x3);

    //为中断设置中断处理函数
    status = XScuGic_Connect(int_ins_ptr, tx_intr_id,
    (Xil_InterruptHandler) tx_intr_handler, axidma_ptr);
    if (status != XST_SUCCESS) {
    return status;
    }

    status = XScuGic_Connect(int_ins_ptr, rx_intr_id,
    (Xil_InterruptHandler) rx_intr_handler, axidma_ptr);
    if (status != XST_SUCCESS) {
    return status;
    }

    XScuGic_Enable(int_ins_ptr, tx_intr_id);
    XScuGic_Enable(int_ins_ptr, rx_intr_id);

    //启用来自硬件的中断
    Xil_ExceptionInit();
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
    (Xil_ExceptionHandler) XScuGic_InterruptHandler,
    (void *) int_ins_ptr);
    Xil_ExceptionEnable();

    //使能DMA中断
    XAxiDma_IntrEnable(&axidma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DMA_TO_DEVICE);
    XAxiDma_IntrEnable(&axidma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DEVICE_TO_DMA);

    return XST_SUCCESS;
    }

    //此函数禁用DMA引擎的中断
    static void disable_intr_system(XScuGic * int_ins_ptr, u16 tx_intr_id,
    u16 rx_intr_id)
    {
    XScuGic_Disconnect(int_ins_ptr, tx_intr_id);
    XScuGic_Disconnect(int_ins_ptr, rx_intr_id);
    }

4.实验结果

  • 终端显示

    image-20231002154115335
欢迎来到ssy的世界